package cn.com.surker.common;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.*;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;


import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * HTTP请求帮助类
 */
@Slf4j
public class HttpRequestHelper {

	//请求超时时间,这个时间定义了socket读数据的超时时间，也就是连接到服务器之后到从服务器获取响应数据需要等待的时间,发生超时，会抛出SocketTimeoutException异常。
	private static final int SOCKET_TIME_OUT = 60000;
	//连接超时时间,这个时间定义了通过网络与服务器建立连接的超时时间，也就是取得了连接池中的某个连接之后到接通目标url的连接等待时间。发生超时，会抛出ConnectionTimeoutException异常
	private static final int CONNECT_TIME_OUT = 60000;
	private static boolean ignoreSSL = true;

	/**
	 * 向指定URL发送GET方法的请求
	 *
	 * @param url   发送请求的URL
	 * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
	 * @return URL 所代表远程资源的响应结果
	 */
	public static String sendGet(String url, String param) {
		String result = "";
		BufferedReader in = null;
		try {
			String urlNameString = url + "?" + param;
			URL realUrl = new URL(urlNameString);
			URLConnection connection = realUrl.openConnection();
			connection.setRequestProperty("accept", "*/*");
			connection.setRequestProperty("connection", "Keep-Alive");
			connection.setRequestProperty("user-agent",
				"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
			connection.connect();
			in = new BufferedReader(new InputStreamReader(
				connection.getInputStream(), "utf-8"));
			String line = "";
			while ((line = in.readLine()) != null) {
				result += line;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			try {
				if (in != null) {
					in.close();
				}
			} catch (Exception e2) {
				e2.printStackTrace();
				return null;
			}
		}
		return result;
	}

	/**
	 * 向指定 URL 发送POST方法的请求
	 *
	 * @param url   发送请求的 URL
	 * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
	 * @return 所代表远程资源的响应结果
	 */
	public static String sendPost(String url, String param) {
		PrintWriter out = null;
		BufferedReader in = null;
		String result = "";
		try {
			URL realUrl = new URL(url);
			URLConnection conn = realUrl.openConnection();
			conn.setRequestProperty("accept", "*/*");
			conn.setRequestProperty("connection", "Keep-Alive");
			conn.setRequestProperty("user-agent",
				"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
			conn.setDoOutput(true);
			conn.setDoInput(true);

			if (param != null && !"".equals(param)) {
				out = new PrintWriter(conn.getOutputStream());
				out.print(param);
				out.flush();
			}

			in = new BufferedReader(
				new InputStreamReader(conn.getInputStream(), "utf-8"));
			String line = "";
			while ((line = in.readLine()) != null) {
				result += line;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		} finally {
			try {
				if (out != null) {
					out.close();
				}
				if (in != null) {
					in.close();
				}
			} catch (IOException ex) {
				ex.printStackTrace();
				return null;
			}
		}
		return result;
	}

	/**
	 * <p>传入参数为JSON，以POST方式请求</p>
	 *
	 * @param url  请求链接
	 * @param json JSON格式的传入参数
	 * @return 返回 返回数据
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static String sendJsonPost(String url, String json) throws ClientProtocolException, IOException {
		CloseableHttpClient httpclient = HttpClientBuilder.create().build();
		HttpPost httppost = new HttpPost(url);
		if (json != null && !"".equals(json)) {
			httppost.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
		}

		CloseableHttpResponse response = httpclient.execute(httppost);
		HttpEntity entity = response.getEntity();
		String str = EntityUtils.toString(entity, "utf-8");

		httppost.releaseConnection();
		if (response.getStatusLine().getStatusCode() == 200) {
			return str;
		} else {
			return null;
		}
	}


	/**
	 * 发送HttpPost请求
	 *
	 * @param strURL 服务地址
	 * @param json   json字符串,例如: "{ \"id\":\"12345\" }" ;其中属性名必须带双引号<br/>
	 * @return 成功:返回json字符串<br/>
	 */
	public static String senPostJson(String strURL, String json) {
		try {
			URL url = new URL(strURL);// 创建连接
			HttpURLConnection connection = (HttpURLConnection) url
				.openConnection();
			connection.setDoOutput(true);
			connection.setDoInput(true);
			connection.setUseCaches(false);
			connection.setInstanceFollowRedirects(true);
			connection.setRequestMethod("POST"); // 设置请求方式
			connection.setRequestProperty("Accept", "application/json"); // 设置接收数据的格式
			connection.setRequestProperty("Content-Type", "application/json"); // 设置发送数据的格式
			connection.connect();
			OutputStreamWriter out = new OutputStreamWriter(
				connection.getOutputStream(), "UTF-8"); // utf-8编码
			out.append(json);
			out.flush();
			out.close();
			// 读取响应
			int length = (int) connection.getContentLength();// 获取长度
			InputStream is = connection.getInputStream();
			if (length != -1) {
				byte[] data = new byte[length];
				byte[] temp = new byte[512];
				int readLen = 0;
				int destPos = 0;
				while ((readLen = is.read(temp)) > 0) {
					System.arraycopy(temp, 0, data, destPos, readLen);
					destPos += readLen;
				}
				String result = new String(data, "UTF-8"); // utf-8编码
				System.out.println(result);
				return result;
			}
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
		return null;
	}

	public static final String get(URI url) {
		// 创建链接
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpGet httpGet = new HttpGet(url);
		try {
			// 执行请求
			CloseableHttpResponse response = httpClient.execute(httpGet);
			HttpEntity entity = response.getEntity();
			// 响应结果
			String res = null;
			if (entity != null)
				res = EntityUtils.toString(entity);

			// 销毁
			EntityUtils.consume(entity);
			response.close();

			return res;
		} catch (ClientProtocolException cp) {
			throw new IllegalArgumentException("不支持的请求协议 - " + cp.getMessage());
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				httpClient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * @param url
	 * @return
	 */
	public static final String get(String url) {
		// 创建链接
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpGet httpGet = new HttpGet(url);
		try {
			// 执行请求
			CloseableHttpResponse response = httpClient.execute(httpGet);
			HttpEntity entity = response.getEntity();
			// 响应结果
			String res = null;
			if (entity != null)
				res = EntityUtils.toString(entity);

			// 销毁
			EntityUtils.consume(entity);
			response.close();

			return res;
		} catch (ClientProtocolException cp) {
			throw new IllegalArgumentException("不支持的请求协议 - " + cp.getMessage());
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				httpClient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 发送POST请求
	 *
	 * @param url    地址
	 * @param params 参数
	 * @return
	 */
	public static final String post(String url, Map<String, Object> params) {
		// 创建客户端
		CloseableHttpClient httpClient = HttpClients.createDefault();
		// 创建POST请求
		HttpPost httpPost = new HttpPost(url);

		List<NameValuePair> pairs = new ArrayList<NameValuePair>();

		if (params != null && !params.isEmpty()) {
			Iterator<Map.Entry<String, Object>> iterator = params.entrySet().iterator();
			while (iterator.hasNext()) {
				Map.Entry<String, Object> entry = iterator.next();
				pairs.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
			}
		}

		try {
			// HTTP 表单实体
			HttpEntity json = new UrlEncodedFormEntity(pairs, "utf-8");
			httpPost.setEntity(json);

			// 执行请求
			CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
			// 响应实体
			HttpEntity responseEntity = httpResponse.getEntity();

			String res = null;
			if (responseEntity != null)
				res = EntityUtils.toString(responseEntity, "utf-8");
			// 销毁
			EntityUtils.consume(responseEntity);
			httpResponse.close();
			// 返回
			return res;

		} catch (UnsupportedEncodingException ue) {
			throw new IllegalArgumentException("不支持的字符编码 - " + ue.getMessage());
		} catch (ClientProtocolException cp) {
			throw new IllegalArgumentException("不支持的请求协议 - " + cp.getMessage());
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				httpClient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 上传文件
	 *
	 * @param url    地址
	 * @param files  文件集合
	 * @param params 参数集合
	 * @return
	 */
	public static final String upload(String url, Map<String, File> files, Map<String, String> params) {
		// 创建客户端
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpPost httpPost = new HttpPost(url);

		MultipartEntityBuilder builder = MultipartEntityBuilder.create();

		// 添加文件
		if (files != null && !files.isEmpty()) {
			Iterator<Map.Entry<String, File>> iterator = files.entrySet().iterator();
			while (iterator.hasNext()) {
				Map.Entry<String, File> entry = iterator.next();
				builder.addPart(entry.getKey(), new FileBody(entry.getValue()));
			}
		}
		// 添加其他参数
		if (params != null && !params.isEmpty()) {
			Iterator<Map.Entry<String, String>> iterator = params.entrySet().iterator();
			while (iterator.hasNext()) {
				Map.Entry<String, String> next = iterator.next();
				builder.addPart(next.getKey(), new StringBody(next.getValue(), ContentType.create("text/plain", Consts.UTF_8)));

			}
		}

		try {
			// 构建请求参数
			HttpEntity request = builder.build();
			httpPost.setEntity(request);
			// 执行请求
			CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
			HttpEntity entity = httpResponse.getEntity();
			// 处理响应结果
			String res = null;
			if (entity != null)
				res = EntityUtils.toString(entity);
			// 销毁
			EntityUtils.consume(entity);
			httpResponse.close();
			// 返回结果
			return res;
		} catch (UnsupportedEncodingException ue) {
			throw new IllegalArgumentException("不支持的字符编码 - " + ue.getMessage());
		} catch (ClientProtocolException cp) {
			throw new IllegalArgumentException("不支持的请求协议 - " + cp.getMessage());
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			try {
				httpClient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 下载文件
	 *
	 * @param url    文件地址
	 * @param target 存储地址
	 */
	public static final void download(String url, String target) {
		// 客户端
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpGet httpGet = new HttpGet(url);
		InputStream in = null;
		try {
			CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
			HttpEntity entity = httpResponse.getEntity();
			if (entity != null) {
				in = entity.getContent();
				File storage = new File(target);
				// 创建目录
				boolean exists = true;
				if (!storage.getParentFile().exists())
					exists = storage.getParentFile().mkdirs();
				if (!exists)
					throw new IllegalArgumentException("Make directory " + storage.getParentFile().getPath() + "failed.");
				OutputStream out = null;
				try {
					out = new FileOutputStream(storage);

					// 写入磁盘
					byte[] buffer = new byte[1024];
					int len;
					while ((len = in.read(buffer)) != -1)
						out.write(buffer, 0, len);
					out.flush();
					out.close();
				} finally {
					if (out != null) out.close();
				}
			}
			// 销毁
			EntityUtils.consume(entity);
			httpResponse.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				httpClient.close();
				if (in != null) in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 下载文件
	 *
	 * @param url 文件地址
	 * @return 字节数组
	 */
	public static final byte[] download(String url) {
		// 客户端
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpGet httpGet = new HttpGet(url);
		byte[] bytes = null;
		try {
			CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
			HttpEntity entity = httpResponse.getEntity();
			if (entity != null) {
				InputStream in = entity.getContent();

				ByteArrayOutputStream out = new ByteArrayOutputStream();

				// 写入磁盘
				byte[] buffer = new byte[1024];
				int len = 0;
				while ((len = in.read(buffer)) != -1)
					out.write(buffer, 0, len);
				bytes = out.toByteArray();
				in.close();
				out.close();
			}
			// 销毁
			EntityUtils.consume(entity);
			httpResponse.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				httpClient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return bytes;
	}

	/**
	 * 发送GET请求
	 *
	 * @param url        目的地址
	 * @param parameters 请求参数，Map类型。
	 * @return 远程响应结果
	 */
	public static String get(String url, Map<String, String> parameters) {
		String result = "";

		// 读取响应输入流
		BufferedReader in = null;

		// // 存储参数
		StringBuffer sb = new StringBuffer();

		try {
			String full_url = url;
			// 创建URL对象
			URL connURL = new URL(full_url);
			// 打开URL连接
			HttpURLConnection httpConn = (HttpURLConnection) connURL
				.openConnection();
			// 设置通用属性
			httpConn.setRequestProperty("Accept", "*/*");
			httpConn.setRequestProperty("Connection", "Keep-Alive");
			httpConn.setRequestProperty("User-Agent",
				"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");
			// 建立实际的连接
			httpConn.connect();
			// 响应头部获取
			Map<String, List<String>> headers = httpConn.getHeaderFields();
			// 遍历所有的响应头字段
			for (String key : headers.keySet()) {
				System.out.println(key + "\t：\t" + headers.get(key));
			}
			// 定义BufferedReader输入流来读取URL的响应,并设置编码方式
			in = new BufferedReader(new InputStreamReader(httpConn
				.getInputStream(), "UTF-8"));
			String line;
			// 读取返回的内容
			while ((line = in.readLine()) != null) {
				result += line;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (in != null) {
					in.close();
				}
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}
		return result;
	}


	/**
	 * 发送  post 请求
	 *
	 * @param url   请求地址，如 http://www.baidu.com
	 * @param param 模拟form 提交
	 * @return
	 * @throws Exception
	 */
	public static String postForAPP(String url, Map<String, Object> headers, Map<String, Object> param) throws Exception {
		//目前HttpClient最新版的实现类为CloseableHttpClient
/*
        CloseableHttpClient client = HttpClients.createDefault();
*/
		CloseableHttpClient client = getHttpsClient();

		CloseableHttpResponse response = null;
		HttpEntity entity = null;
		try {
			if (param != null) {
				//建立Request的对象，一般用目标url来构造，Request一般配置addHeader、setEntity、setConfig
				HttpPost req = new HttpPost(url);
				log.info("转换后的json参数:{}", JSONObject.toJSONString(param));
				entity = new StringEntity(JSONObject.toJSONString(param), "utf-8");
				log.info("请求参数:{}", JSONObject.toJSONString(entity));
				//setHeader,添加头文件
				if (headers != null) {
					Set<String> keys = headers.keySet();
					for (String key : keys) {
						req.setHeader(key, headers.get(key).toString());
					}
				}
				//setConfig,添加配置,如设置请求超时时间,连接超时时间
				RequestConfig reqConfig = RequestConfig.custom().setSocketTimeout(SOCKET_TIME_OUT).setConnectTimeout(CONNECT_TIME_OUT).build();
				req.setConfig(reqConfig);
				//setEntity,添加内容
				req.setEntity(entity);
				//执行Request请求,CloseableHttpClient的execute方法返回的response都是CloseableHttpResponse类型
				//其常用方法有getFirstHeader(String)、getLastHeader(String)、headerIterator（String）取得某个Header name对应的迭代器、getAllHeaders()、getEntity、getStatus等
				response = client.execute(req);
				entity = response.getEntity();
				//用EntityUtils.toString()这个静态方法将HttpEntity转换成字符串,防止服务器返回的数据带有中文,所以在转换的时候将字符集指定成utf-8就可以了
				String result = EntityUtils.toString(entity, "UTF-8");
				log.info("-------------------------返回结果:" + result + "-------------");
				if (response.getStatusLine().getStatusCode() == 200) {
					log.error(result + "-----------success------------------");
					return result;
				} else {
					log.error(response.getStatusLine().getStatusCode() + "------------------fail-----------");
					return null;
				}
			}
			return null;
		} catch (Exception e) {
			log.error("--------------------------post error: ", e);
			throw new Exception();
		} finally {
			//一定要记得把entity fully consume掉，否则连接池中的connection就会一直处于占用状态
			EntityUtils.consume(entity);
			log.error("---------------------------finally-------------");
			System.out.println("---------------------------------------------------");
		}
	}

	public static CloseableHttpClient getHttpsClient() {

		CloseableHttpClient httpClient;
		if (ignoreSSL) {//ignoreSSL为true时，绕过证书
			SSLContext sslContext = null;
			try {
				sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {
					@Override
					public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
						return true;
					}
				}).build();
			} catch (NoSuchAlgorithmException e) {
				e.getStackTrace();
			} catch (KeyManagementException e) {
				e.getStackTrace();
			} catch (KeyStoreException e) {
				e.getStackTrace();
			}
			httpClient = HttpClients.custom().setSSLContext(sslContext).
				setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
		} else {
			httpClient = HttpClients.createDefault();
		}
		return httpClient;
	}

}
